using System;
using System.Threading;
using java.util;

namespace org.jawk.jrt
{
	/// <summary>
	/// Manages multiple blocking code segments simultaneously such that
	/// unblocking one block condition releases the block of all other
	/// block code segments.
	/// </summary>
	/// <remarks>
	/// Manages multiple blocking code segments simultaneously such that
	/// unblocking one block condition releases the block of all other
	/// block code segments.
	/// </remarks>
	/// <seealso cref="BlockObject">BlockObject</seealso>
	/// <seealso cref="BulkBlockObject">BulkBlockObject</seealso>
	public class BlockManager
	{
		/// <summary>
		/// Executes all block segments simultaneously, waiting for
		/// one block release.
		/// </summary>
		/// <remarks>
		/// Executes all block segments simultaneously, waiting for
		/// one block release.
		/// <p>
		/// The algorithm is as follows:
		/// <ul>
		/// <li>Collect linked block objects into a List.
		/// <li>Spawn a BlockThread for each block object.
		/// <li>Wait for notification from any of the BlockThreads.
		/// <li>Interrupt remaining block threads.
		/// <li>Wait for each BlockThread to die.
		/// <li>Return the block object notifier which satisified their block condition.
		/// </ul>
		/// <p>
		/// And, the BlockThread algorithm is as follows:
		/// <p>
		/// <ul>
		/// <li>try, catch for InterruptedException ...
		/// <ul>
		/// <li>Execute the BlockObject block segment.
		/// <li>Assign the notifier from this BlockObject
		/// if one isn't already assigned (to mitigate
		/// a race condition).
		/// <li>Notify the BlockManager.
		/// </ul>
		/// <li>If interrupted, do nothing and return.
		/// </ul>
		/// </remarks>
		/// <param name="bo">
		/// BlockObject to employ.  Other block objects
		/// may be linked to this block object.  In this event,
		/// employ all block objects simultaneously.
		/// </param>
		public virtual string Block(org.jawk.jrt.BlockObject bo)
		{
			// get all block objects
			System.Collections.Generic.IList<org.jawk.jrt.BlockObject> bos = bo.GetBlockObjects
				();
			// each block object contains a wait statement
			// (either indefinite or timed)
			// for each block object
			// 	spawn a thread (preferably using a threadpool)
			// 	do the wait
			//	signal a break in the block
			// interrupt all other threads, resulting in InterruptedExceptions
			System.Collections.Generic.LinkedList<IThread> thread_list = new System.Collections.Generic.LinkedList
				<IThread>();
			lock (this)
			{
				foreach (org.jawk.jrt.BlockObject blockobj in bos)
				{
					// spawn a thread
					IThread t = new org.jawk.jrt.BlockManager.BlockThread(this, blockobj);
					t.Start();
					thread_list.AddLast(t);
				}
				// now, wait for notification from one of the BlockThreads
				try
				{
					Monitor.Wait(this);
				}
				catch (System.Exception)
				{
				}
			}
			// block successful, interrupt other blockers
			// and wait for thread deaths
			foreach (IThread t in thread_list)
			{
				t.Interrupt();
				try
				{
					t.Join();
				}
				catch (System.Exception)
				{
				}
			}
			// return who was the notifier
			//assert notifier != null;
			return notifier;
		}

		private object NOTIFIER_LOCK = "NOTIFIER_LOCK";

		private string notifier = null;

		private class BlockThread : IThread
		{
			private org.jawk.jrt.BlockObject bo;

			public BlockThread(BlockManager _enclosing, org.jawk.jrt.BlockObject bo)
			{
                mThread = new Thread(new ThreadStart(Run));
				this._enclosing = _enclosing;
				mThread.Name = "BlockThread for " + bo.GetNotifierTag();
				this.bo = bo;
			}

			public void Run()
			{
				try
				{
					this.bo.Block();
					lock (this._enclosing.NOTIFIER_LOCK)
					{
						if (this._enclosing.notifier == null)
						{
							this._enclosing.notifier = this.bo.GetNotifierTag();
						}
					}
					lock (this._enclosing)
					{
						Monitor.Pulse(this._enclosing);
					}
				}
				catch (System.Exception re)
				{
					Console.Error.WriteLine(re);
					System.Console.Error.WriteLine("(exitting)");
					System.Environment.Exit(1);
				}
			}

			private readonly BlockManager _enclosing;

            #region IThread Members

            private Thread mThread;
            public void Start()
            {
                mThread.Start();
            }

            public void SetDaemon(bool b)
            {
                mThread.IsBackground = b;
            }

            #endregion

            #region IJoinable Members

            public void Join()
            {
                mThread.Join();
            }

            public void Interrupt()
            {
                mThread.Interrupt();
            }

            #endregion

            #region IBlockable Members

            [Obsolete("Not Implemented", true)]
            public bool WillBlock(BlockObject bo)
            {
                return false;
            }

            #endregion
        }
	}
}
